<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>javascript</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box
    }
    body {
      font-size: 14px;
      padding: 20px;
    }
    ul li {
      line-height: 30px;
    }
  </style>
</head>
<body>
  <div id="app">
    <div>{{name}}</div>
    <div>{{age}}</div>
  </div>
</body>
<script>
  // https://juejin.im/post/5abdd6f6f265da23793c4458
  let obj = {
    a: 1
  }
  var val = obj.a
  Object.defineProperty(obj, 'a', {
    configurable: true,
    get: function() {
      return val
    },
    set: function(newVal) {
      val = newVal
    }
  })
  obj.a = 12
  console.log(obj)
  /**
   * 定义wue 
   */
  function Wue(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data
    // 数据劫持
    Observe(data)
    // 数据访问代理
    for (let key in data) {
      Object.defineProperty(this, key, {
        configurable: true,
        get: function() {
          return this._data[key]
        },
        set: function(newVal) {
          this._data[key] = newVal
        }
      })
    }
    new Compile(options.el, this)
  }

  // 数据劫持
  function Observe(data) {
    for (let key in data) {
      let val = data[key]
      observe(val) // 如果是对象继续想下找，实现深度的数据劫持
      Object.defineProperty(data, key, {
        configurable: true,
        get: function() {
          console.log('您访问了'+key)
          return val
        },
        set: function(newVal) {
          console.log('您设置了'+key)
          if (newVal == val) {
            return
          }
          val = newVal
          observe(val)
        }
      })
    }
  }
  // 判断是否需要数据劫持
  function observe(data) {
    if (!data || typeof data !== 'object') return
    return new Observe(data)
  }
  var wm = new Wue({
    el: '#app',
    data: {
      name: 'zhangli',
      age: '29'
    }
  })
  wm.name = 123
console.dir(wm)
// 创建Compile构造函数
function Compile(el, vm) {
    // 将el挂载到实例上方便调用
    vm.$el = document.querySelector(el);
    // 在el范围里将内容都拿到，当然不能一个一个的拿
    // 可以选择移到内存中去然后放入文档碎片中，节省开销
    let fragment = document.createDocumentFragment();
    
    while (child = vm.$el.firstChild) {
        fragment.appendChild(child);    // 此时将el中的内容放入内存中
    }
    // 对el里面的内容进行替换
    function replace(frag) {
        Array.from(frag.childNodes).forEach(node => {
            let txt = node.textContent;
            let reg = /\{\{(.*?)\}\}/g;   // 正则匹配{{}}
            
            if (node.nodeType === 3 && reg.test(txt)) { // 即是文本节点又有大括号的情况{{}}
                console.log(RegExp.$1); // 匹配到的第一个分组 如： a.b, c
                let arr = RegExp.$1.split('.');
                let val = vm;
                arr.forEach(key => {
                    val = val[key];     // 如this.a.b
                });
                // 用trim方法去除一下首尾空格
                node.textContent = txt.replace(reg, val).trim();
            }
            // 如果还有子节点，继续递归replace
            if (node.childNodes && node.childNodes.length) {
                replace(node);
            }
        });
    }
    
    replace(fragment);  // 替换内容
    vm.$el.appendChild(fragment);   // 再将文档碎片放入el中
}
setTimeout(() => {
  wm.name = 'haha'
},2000)
</script>
</html>